﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.Management;
using ProgNetComponents.Logs;

namespace System
{
    /// <summary>
    /// ProcessInfo
    /// </summary>
    public enum ProcessInfo
    {
        /// <summary>
        /// Caption
        /// </summary>
        Caption,
        /// <summary>
        /// CommandLine
        /// </summary>
        CommandLine,
        /// <summary>
        /// CreationClassName
        /// </summary>
        CreationClassName,
        /// <summary>
        /// CreationDate
        /// </summary>
        CreationDate,
        /// <summary>
        /// CSCreationClassName
        /// </summary>
        CSCreationClassName,
        /// <summary>
        /// CSName
        /// </summary>
        CSName,
        /// <summary>
        /// Description
        /// </summary>
        Description,
        /// <summary>
        /// ExecutablePath
        /// </summary>
        ExecutablePath,
        /// <summary>
        /// ExecutionState
        /// </summary>
        ExecutionState,
        /// <summary>
        /// Handle
        /// </summary>
        Handle,
        /// <summary>
        /// HandleCount
        /// </summary>
        HandleCount,
        /// <summary>
        /// InstallDate
        /// </summary>
        InstallDate,
        /// <summary>
        /// KernelModeTime
        /// </summary>
        KernelModeTime,
        /// <summary>
        /// MaximumWorkingSetSize
        /// </summary>
        MaximumWorkingSetSize,
        /// <summary>
        /// MinimumWorkingSetSize
        /// </summary>
        MinimumWorkingSetSize,
        /// <summary>
        /// Name
        /// </summary>
        Name,
        /// <summary>
        /// OSCreationClassName
        /// </summary>
        OSCreationClassName,
        /// <summary>
        /// OSName
        /// </summary>
        OSName,
        /// <summary>
        /// OtherOperationCount
        /// </summary>
        OtherOperationCount,
        /// <summary>
        /// OtherTransferCount
        /// </summary>
        OtherTransferCount,
        /// <summary>
        /// PageFaults
        /// </summary>
        PageFaults,
        /// <summary>
        /// PageFileUsage
        /// </summary>
        PageFileUsage,
        /// <summary>
        /// ParentProcessId
        /// </summary>
        ParentProcessId,
        /// <summary>
        /// PeakPageFileUsage
        /// </summary>
        PeakPageFileUsage,
        /// <summary>
        /// PeakVirtualSize
        /// </summary>
        PeakVirtualSize,
        /// <summary>
        /// PeakWorkingSetSize
        /// </summary>
        PeakWorkingSetSize,
        /// <summary>
        /// Priority
        /// </summary>
        Priority,
        /// <summary>
        /// PrivatePageCount
        /// </summary>
        PrivatePageCount,
        /// <summary>
        /// ProcessId
        /// </summary>
        ProcessId,
        /// <summary>
        /// QuotaNonPagedPoolUsage
        /// </summary>
        QuotaNonPagedPoolUsage,
        /// <summary>
        /// QuotaPagedPoolUsage
        /// </summary>
        QuotaPagedPoolUsage,
        /// <summary>
        /// QuotaPeakNonPagedPoolUsage
        /// </summary>
        QuotaPeakNonPagedPoolUsage,
        /// <summary>
        /// QuotaPeakPagedPoolUsage
        /// </summary>
        QuotaPeakPagedPoolUsage,
        /// <summary>
        /// ReadOperationCount
        /// </summary>
        ReadOperationCount,
        /// <summary>
        /// ReadTransferCount
        /// </summary>
        ReadTransferCount,
        /// <summary>
        /// SessionId
        /// </summary>
        SessionId,
        /// <summary>
        /// Status
        /// </summary>
        Status,
        /// <summary>
        /// TerminationDate
        /// </summary>
        TerminationDate,
        /// <summary>
        /// ThreadCount
        /// </summary>
        ThreadCount,
        /// <summary>
        /// UserModeTime
        /// </summary>
        UserModeTime,
        /// <summary>
        /// VirtualSize
        /// </summary>
        VirtualSize,
        /// <summary>
        /// WindowsVersion
        /// </summary>
        WindowsVersion,
        /// <summary>
        /// WorkingSetSize
        /// </summary>
        WorkingSetSize,
        /// <summary>
        /// WriteOperationCount
        /// </summary>
        WriteOperationCount,
        /// <summary>
        /// WriteTransferCount
        /// </summary>
        WriteTransferCount,
    }

    /// <summary>
    /// Extension methods for Process class
    /// </summary>
    public static class ProcessExt
    {
        /// <summary>
        /// _log
        /// </summary>
        static Logger _log = new Logger(typeof(ProcessExt));

        /// <summary>
        /// Suspends the specified process.
        /// </summary>
        /// <param name="process">The process.</param>
        /// <returns></returns>
        public static bool Suspend(this Process process)
        {
            bool result = true;
            try
            {
                if (process.ProcessName.IsNullOrEmpty())
                    return false;
                foreach (ProcessThread pT in process.Threads)
                {
                    IntPtr pOpenThread = Kernel32.OpenThread(Kernel32.ThreadAccess.SUSPEND_RESUME, false, (uint)pT.Id);

                    if (pOpenThread == IntPtr.Zero)
                    {
                        break;
                    }

                    Kernel32.SuspendThread(pOpenThread);
                }
            }
            catch (Exception ex)
            {
                ex.LogInternal();
                result = false;
            }
            return result;
        }
        /// <summary>
        /// Resumes the specified process.
        /// </summary>
        /// <param name="process">The process.</param>
        /// <returns></returns>
        public static bool Resume(this Process process)
        {
            bool result = true;
            try
            {
                if (process.ProcessName.IsNullOrEmpty())
                    return false;

                foreach (ProcessThread pT in process.Threads)
                {
                    IntPtr pOpenThread = Kernel32.OpenThread(Kernel32.ThreadAccess.SUSPEND_RESUME, false, (uint)pT.Id);

                    if (pOpenThread == IntPtr.Zero)
                    {
                        break;
                    }

                    Kernel32.ResumeThread(pOpenThread);
                }
            }
            catch (Exception ex)
            {
                ex.LogInternal();
                result = false;
            }
            return result;
        }
        /// <summary>
        /// Gets the process information.
        /// </summary>
        /// <param name="process">The process.</param>
        /// <param name="info">The information.</param>
        /// <returns></returns>
        public static object GetProcessInfo(this Process process, ProcessInfo info)
        {
            object res = null;
            try
            {
                string field = info.ToString();
                string query = "SELECT " + field + " FROM Win32_Process WHERE ProcessId = " + process.Id;
                using (ManagementObjectSearcher mos = new ManagementObjectSearcher(query))
                {
                    foreach (ManagementObject mo in mos.Get())
                    {
                        res = mo[field];
                        break;
                    }
                }
            }
            catch (Exception ex)
            {
                ex.LogInternal();
            }
            return res;
        }
        /// <summary>
        /// Brings the window to front.
        /// </summary>
        /// <param name="process">The process.</param>
        public static void BringWindowToFront(this Process process)
        {
            if (process.MainWindowHandle != IntPtr.Zero)
            {
                try
                {
                    if (User32.IsIconic(process.MainWindowHandle) == 1)
                    {
                        User32.ShowWindow(process.MainWindowHandle, User32.SW_RESTORE);
                    }
                    User32.SetForegroundWindow(process.MainWindowHandle);
                }
                catch { }
            }
        }
        /// <summary>
        /// Gets the child proceses.
        /// </summary>
        /// <param name="process">The process.</param>
        /// <returns></returns>
        public static List<Process> GetChildProceses(this Process process)
        {
            List<Process> child = new List<Process>();
            string query = "SELECT ProcessId, CreationDate FROM Win32_Process WHERE ParentProcessId = " + process.Id;
            int id = -1;
            Process pr;
            using (ManagementObjectSearcher mos = new ManagementObjectSearcher(query))
            {
                foreach (ManagementObject mo in mos.Get())
                {
                    id = mo["ProcessId"].AsInt(-1);
                    if (ManagementDateTimeConverter.ToDateTime(mo["CreationDate"].AsString()) >= process.StartTime)
                    {
                        if (id != -1)
                        {
                            try
                            {
                                pr = Process.GetProcessById(id);
                                if (pr != null)
                                    child.Add(pr);
                            }
                            catch { }
                        }
                    }
                }
            }
            return child;
        }
        /// <summary>
        /// Gets all child proceses.
        /// </summary>
        /// <param name="process">The process.</param>
        /// <returns></returns>
        public static List<Process> GetAllChildProceses(this Process process)
        {
            List<Process> child = new List<Process>();
            string query = "SELECT ProcessId, CreationDate FROM Win32_Process WHERE ParentProcessId = " + process.Id;
            int id = -1;
            Process pr;
            try
            {
                using (ManagementObjectSearcher mos = new ManagementObjectSearcher(query))
                {
                    foreach (ManagementObject mo in mos.Get())
                    {
                        id = mo["ProcessId"].AsInt(-1);
                        if (ManagementDateTimeConverter.ToDateTime(mo["CreationDate"].AsString()) >= process.StartTime)
                        {
                            if (id != -1)
                            {
                                try
                                {
                                    pr = Process.GetProcessById(id);
                                    if (pr != null)
                                        child.Add(pr);
                                }
                                catch { }
                            }
                        }
                    }
                }
            }
            catch
            {

            }
            Process[] prc = child.ToArray();
            foreach (Process pr2 in prc)
            {
                child.AddRange(pr2.GetAllChildProceses());
            }
            return child;
        }
        /// <summary>
        /// Kills the process tree.
        /// </summary>
        /// <param name="process">The process.</param>
        /// <param name="suspend">if set to <c>true</c> [suspend].</param>
        /// <param name="logKilledProcesses">if set to <c>true</c> [log killed processes].</param>
        public static void KillProcessTree(this Process process, bool suspend = false, bool logKilledProcesses = false)
        {
            try
            {
                if (suspend)
                {
                    try
                    {
                        _log.LogInternal("Suspending process " + process.ProcessName + " " + process.GetProcessInfo(ProcessInfo.CommandLine));
                        process.Suspend();
                    }
                    catch (Exception ex) { ex.LogInternal("Exception suspending process"); }
                }
                List<Process> children = process.GetAllChildProceses();
                children.Reverse();
                foreach (Process p in children)
                {
                    try
                    {
                        if (!p.HasExited && p.ProcessName != "explorer")
                        {
                            if (logKilledProcesses)
                                ("Killing: " + p.ProcessName + ", " + p.StartInfo.FileName).LogInternal();
                            _log.LogInfo("Killing process " + p.ProcessName + " " + p.GetProcessInfo(ProcessInfo.CommandLine));
                            p.Kill();

                        }
                    }
                    catch { }
                }
                try
                {
                    if (!process.HasExited && process.ProcessName != "exprorer")
                    {
                        if (logKilledProcesses)
                            ("Killing: " + process.ProcessName + ", " + process.StartInfo.FileName).LogInternal();
                        _log.LogInfo("Killing process " + process.ProcessName + " " + process.GetProcessInfo(ProcessInfo.CommandLine));
                            
                        process.Kill();
                    }
                }
                catch (InvalidOperationException)
                {
                   //the process has exited
                }
                catch (Exception ex)
                {
                    ex.LogInternal("Exception killing process");
                }
                children.Clear();
                children = null;
            }
            catch (Exception ex)
            {
                ex.LogInternal("KillProcessTree");
            }
        }
    }
}
